#include "glwidget.h"


GLWidget::GLWidget(QWidget *parent) :
    QGLWidget(parent)
{
    tEye = Angel::RotateX(0.0); //trackball eye;
    tb_start = true;
    motion = false;

	gameState = GAME_START;
    controllerMode = MODE_VERTICAL;

    g_ObjectDefaultColor = vec4(0.5, 0.5, 0.5, 1.0);
    g_ObjectSelectionColor = vec4(1.0, 1.0, 0.0, 1.0);
    g_ObjectCollisionColor = vec4(1.0, 0.0, 0.0, 1.0);
    g_ObjectBoundColor= vec4(0.0, 0.2, 0.6, 0.4);


    g_LightAmbient = vec4(0.2, 0.2, 0.2, 1.0);
    g_LightDiffuse = vec4(1.0, 1.0, 1.0, 1.0);
    g_LightSpecular = vec4(1.0, 1.0, 1.0, 1.0);
    g_LightPos = vec4(10.0, 10.0, 20.0, 1.0);

    g_BVVisibleFlag = true;

    fingerFactor = 1;
    nowControl = 0;

    double abc =10;
    xLen = abc;
    yLen = abc*30;
    zLen = abc;

    rs[0] = 0.0; rs[1] = 0.0; rs[2] = 0.0;
    ts[0] = 0.0; ts[1] = 2.2; ts[2] = 0.0;
    ss[0] = 0.7; ss[1] = 0.7; ss[2] = 0.7;
    default_rs[0] = 0.0; default_rs[1] = 0.0; default_rs[2] = 0.0;
    default_ts[0] = 0.0; default_ts[1] = 2.2; default_ts[2] = 0.0;
    default_ss[0] = 0.7; default_ss[1] = 0.7; default_ss[2] = 0.7;

    numOfObstacles1 = 10;
    clipping = false;
    NUM_OBJECTS = 1+numOfObstacles1;


    g_IntersectionFlag = new bool[11];
    // Update rate
    g_graphicRenderingCounter = 0;
    g_updateRateGraphicElapsedTime = 0.0;
    g_averageUpdateRateGraphicLoop = 1.0;

    /*
     *  at = location of camera; x,y,z, depth
     *  up = ??
     *  cur_eye = ??
     */
    g_ViewMat = LookAt( cur_eye, at, up);


    // Window size
    g_width	= 800;
    g_height = 800;


    //obj_animation
    obj_animation = false;
    objCnt = 0;
    objAdder = 1;


    distort_offset = 0.0f;
    spot_light_position = vec4(0,5,0,1);
    spot_light_lookat = vec4(2,0,0,1);
    directional_light_dir = vec4(1,0,0,0);


    flat_shading= false;
    light_animation = true;
    // Select the shading mode
    shading = 2;

    //light toggles
    direction_toggle = false;
    point_toggle = true;
    spot_toggle = false;

    // Global parameters
    g_theta = 0.0;
    g_magFlag = 1;

    // Buffer ID
    g_VBIDvertices = new GLuint[objN];
    g_VBIDnormals = new GLuint[objN];
    g_IBIDelements = new GLuint[objN];


    at = vec4(airplane.ts[0],airplane.ts[1],airplane.ts[2],0);
    up = vec4(0,1,0,0);
    tEye = Angel::RotateX(0)*Angel::RotateY(180);

    g_bWireFrame = false;

    frame_cnt = 0;
    keyFrame_animation = false;
    iKey = Angel::Translate(ts[0],ts[1],ts[2])*Angel::Scale(ss[0],ss[1],ss[2]);


    rot = 0;

    dof_acc_val = new double[6];
    setFixedSize(800, 800);


    obstacles1 = new Obj3D[numOfObstacles1];

    connect(&timer, SIGNAL(timeout()), this, SLOT(updateGL()));
    timer.start(16);
}


void GLWidget::initializeGL(){
    glutInitDisplayMode		( GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_MULTISAMPLE );
    glClearColor(0.4,0.4,0.7,0);


    glewExperimental = GL_TRUE;
    glewInit				( );
    init();
	

}

void GLWidget::paintGL(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    testIntersection();
    for(int i = 0; i < g_ObjectList.size() ; i++){
        g_ObjectList[i]->updateM();
    }

    drawOBJs();
	if(cameraFollow) restoreView();

    //drawBackground();
    //if(motion)
    trackball();
}


void GLWidget::setf4(const char * uniform_name, float x, float y, float z,float w){
    glUniform4f( glGetUniformLocation( g_IDShader, uniform_name ), x,y,z,w);
}
void GLWidget::setf3(const char * uniform_name, float x, float y, float z){
    glUniform3f( glGetUniformLocation( g_IDShader, uniform_name ),x,y,z);
}
void GLWidget::setf1(const char * uniform_name, float x){
    glUniform1f( glGetUniformLocation( g_IDShader, uniform_name ),x);
}


void GLWidget::keyPressEvent(QKeyEvent *e){

}

void GLWidget::mousePressEvent(QMouseEvent *event){
    if (event->buttons() & Qt::LeftButton) {
        cur_x = event->x();
        cur_y = event->y();
    }
}

void GLWidget::mouseReleaseEvent(QMouseEvent *event){
}

void GLWidget::mouseMoveEvent(QMouseEvent *event){
    if (event->buttons() & Qt::LeftButton) {
        diff_x = cur_x - event->x();
        diff_y = cur_y - event->y();
        cur_x = event->x();
        cur_y = event->y();
        tEye = Angel::RotateX(diff_y*0.1)*Angel::RotateY(diff_x*0.1);
    }

}



////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////



void GLWidget::static_matrix(float rot[3],float trans[3],float scale[3],int idx){
    mat4 staticM = Angel::Translate(trans[0],trans[1],trans[2])*Angel::RotateX(rot[0])*Angel::RotateY(rot[1])*Angel::RotateZ(rot[2])*Angel::Scale(scale[0],scale[1],scale[2]);
    setObjectM(staticM);
}

void GLWidget::addKeyFrame(float rot[3],float trans[3],float scale[3],int frame){
    key_value new_kv;
    new_kv.rotate[0] = rot[0]; new_kv.rotate[1] = rot[1]; new_kv.rotate[2] = rot[2];
    new_kv.translate[0] = trans[0]; new_kv.translate[1] = trans[1]; new_kv.translate[2] = trans[2];
    new_kv.scale[0] = scale[0]; new_kv.scale[1] = scale[1]; new_kv.scale[2] = scale[2];

    kv.push_back(new_kv);
    key_frame.push_back(frame);//vector variable for key frame information
}
mat4 interpolation(float srot[3],float erot[3],float strans[3],float etrans[3],float sscale[3],float escale[3],int frame[2],int tframe){
    float irot[3],itrans[3],iscale[3];
    float ratio = ((float) tframe - (float) frame[0]) / ((float)frame[1]-(float)frame[0]);
    for(int i=0; i<3; i++){
        irot[i] = srot[i] + (erot[i]-srot[i])*ratio;
        itrans[i] = strans[i] + (etrans[i]- strans[i])*ratio;
        iscale[i] = sscale[i]+ (escale[i]-sscale[i])*ratio;
    }

    mat4 interKey = Angel::Translate(itrans[0],itrans[1],itrans[2])*Angel::RotateX(irot[0])*Angel::RotateY(irot[1])*Angel::RotateZ(irot[2])*Angel::Scale(iscale[0],iscale[1],iscale[2]);
    return interKey;
}
void GLWidget::keyFrameAnimate(int frame){
    int start_idx,end_idx;
    int start_frame = 0;
    int end_frame = 0;

    int ff[2]; ff[0] = start_frame; ff[1] = end_frame;

    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mModel" ),1, GL_TRUE, iKey);

    for(int i=0;key_frame[i]<=frame;i++){
        start_frame = key_frame[i]; start_idx =i;
    }
    for(int i=key_frame.size()-1; key_frame[i]>=frame;i--){
        end_frame = key_frame[i];
        end_idx = i;
    }
    ff[0] = start_frame;
    ff[1] = end_frame;
    if(end_frame>frame){
        iKey = interpolation(kv[start_idx].rotate,
			kv[end_idx].rotate,
			kv[start_idx]. translate,
			kv[end_idx].translate,
			kv[start_idx].scale,
			kv[end_idx].scale, 
			ff, frame);
    }
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mModel" ),1, GL_TRUE, iKey);
}
void GLWidget::animate(int idx,int cnt){//obj index, counter
    mat4 MM;//modified matrix
    switch(idx){
    case 0://haed
        //setAndroidM(idx,androidM[idx]);
        break;
    case 1://body
        //setAndroidM(idx,androidM[idx]);
        break;
    case 2://left arm
        break;
    case 3://right arm
        //setAndroidM(idx,MM);
        break;
    case 4://left leg
        //setAndroidM(idx,MM);
        break;
    case 5://right leg
        //setAndroidM(idx,MM);
        break;
    }
}


void GLWidget::readOBJs(){
    airplane.loadFromFile();
    goal.loadFromFile();
    missile.loadFromFile();
    for(int i = 0; i < numOfObstacles1; i++){
        obstacles1[i].loadFromFile();
    }

}

void GLWidget::init( void ){

	cameraFollow = false;
    int index;
    index = g_ObjectList.size() - 1;

    airplane.setObjName("airplane0.obj");
    airplane.setObjID(0);
    airplane.default_ts[0] = 0;
    airplane.default_ts[1] = 0;
    airplane.default_ts[2] = 0;
	airplane.restore();


    goal.setObjName("goal.obj");
    goal.setObjID(11);
    goal.default_ts[0] = -xLen/2;
    goal.default_ts[1] = -zLen/2;
    goal.default_ts[2] = yLen/2;

    missile.setObjName("missile.obj");
    missile.setObjID(11);
    missile.default_ts[0] = airplane.ts[0];
    missile.default_ts[1] = airplane.ts[1];
    missile.default_ts[2] = airplane.ts[2];

    g_ObjectList.push_back(&airplane);
    tEye = Angel::RotateX(0)*Angel::RotateY(180);

    for(int i = 0; i < numOfObstacles1; i++){
        obstacles1[i].setObjName("object1.obj");
        obstacles1[i].setObjID(i+1);

        obstacles1[i].default_ts[0] = (float)(qrand()%100)/100*xLen - xLen/2;
        obstacles1[i].default_ts[1] = (float)(qrand()%100)/100*zLen - zLen/2;
        obstacles1[i].default_ts[2] = (float)(qrand()%100)/100*yLen - xLen/2;

        obstacles1[i].default_ss[0] = (float)(qrand()%100)/100;
        obstacles1[i].default_ss[1] = (float)(qrand()%100)/100;
        obstacles1[i].default_ss[2] = (float)(qrand()%100)/100;

        obstacles1[i].default_rs[0] = (float)(qrand()%100)/100;
        obstacles1[i].default_rs[1] = (float)(qrand()%100)/100;
        obstacles1[i].default_rs[2] = (float)(qrand()%100)/100;

        obstacles1[i].rs[0] = (float)(qrand()%100)/100;
        obstacles1[i].rs[1] = (float)(qrand()%100)/100;
        obstacles1[i].rs[2] = (float)(qrand()%100)/100;

        obstacles1[i].restore();
        obstacles1[i].updateM();

        obstacles1[i].setPosition(vec3(obstacles1[i].ts[0],obstacles1[i].ts[1],obstacles1[i].ts[2]));


        obstacles1[0].default_ts[0] = -xLen/2;
        obstacles1[0].default_ts[1] = -zLen/2;
        obstacles1[0].default_ts[2] = -yLen;
        g_ObjectList.push_back(&obstacles1[i]);
    }
    g_ObjectList.push_back(&goal);
    g_ObjectList.push_back(&missile);

    // Read shaders
    g_IDShader = InitShader( "v_Phong.glsl", "f_Phong.glsl", &g_vertexShaderID, &g_fragmentShaderID );

    glUseProgram	( g_IDShader );
//    glUseProgram(g_RenderingProgramID);

    // Get handles for attributes and uniform variables
    g_RenderingVPositionID = glGetAttribLocation(g_IDShader, "vPosition");
    g_RenderingVNormalID = glGetAttribLocation(g_IDShader, "vNormal");
    g_RenderingProjectionMatID = glGetUniformLocation(g_IDShader, "Projection");
    g_RenderingModelViewMatID = glGetUniformLocation(g_IDShader, "ModelView");

    g_RenderingAmbientID = glGetUniformLocation(g_IDShader, "AmbientProduct");
    g_RenderingDiffuseID = glGetUniformLocation(g_IDShader, "DiffuseProduct");
    g_RenderingSpecularID = glGetUniformLocation(g_IDShader, "SpecularProduct");
    g_RenderingLightPosID = glGetUniformLocation(g_IDShader, "LightPosition");

    glEnable		( GL_DEPTH_TEST );
    glEnable		( GL_MULTISAMPLE );
    glEnable		(GL_CULL_FACE);
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);
    // Load obj-files
    airplane.clearMesh		( );
    for(int i = 0; i < numOfObstacles1; i++){
        obstacles1[i].clearMesh();
    }

    airplane.mesh_name = "unit_cube.obj";
    for(int i = 0; i < numOfObstacles1; i++){
        obstacles1[i].mesh_name = "unit_cube.obj";
    }
    readOBJs();
    airplane.readBound1();


    //makeBackground(background_vertices,background_normals);

    setVAVBObject();



    // Initial settings for uniform variables

    setLight	( );	// uniform vec4
    setModel		( );	// uniform mat4 - block
    setView			( );	// uniform mat4
    setProjection	( );	// uniform mat4

    initAirplaneM();


    // Get handles for attributes and uniform variables
    g_RenderingVPositionID = glGetAttribLocation(g_IDShader, "vPosition");
    g_RenderingVNormalID = glGetAttribLocation(g_IDShader, "vNormal");
    g_RenderingProjectionMatID = glGetUniformLocation(g_IDShader, "Projection");
    g_RenderingModelViewMatID = glGetUniformLocation(g_IDShader, "ModelView");


    g_RenderingAmbientID = glGetUniformLocation(g_IDShader, "AmbientProduct");
    g_RenderingDiffuseID = glGetUniformLocation(g_IDShader, "DiffuseProduct");
    g_RenderingSpecularID = glGetUniformLocation(g_IDShader, "SpecularProduct");
    g_RenderingLightPosID = glGetUniformLocation(g_IDShader, "LightPosition");



    airplane.start();
	

}
void GLWidget::drawBackground(){
    //set diffuse color
    setf4("material0.Kd",0.2,0.2,0.5,8);

    mat4 modelMat =  Angel::Translate(0,3,0)*Angel::Scale(8,8,8)*Angel::RotateY(0);
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mModel" ),
        1, GL_TRUE, modelMat );

    GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
    glEnableVertexAttribArray( vPosition );
    glBindBuffer( GL_ARRAY_BUFFER, g_VBIDbackground);
    glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

    GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
    glEnableVertexAttribArray( vNormal );
    glBindBuffer( GL_ARRAY_BUFFER, g_VBIDbackground_normals);
    glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
    glDrawArrays(GL_TRIANGLES,0,background_vertices.size());
}

void GLWidget::drawOBJ(Obj3D* obj){

    setObjectM(obj->objM);

    //setAndroidM(i,,0.0);

    // Modelview matrix
        mat4 modelMat = Translate(obj->getPosition()) * Scale(1.0, 1.0, 1.0) * RotateY(0.0);

    mat4 modelViewMat = g_ViewMat * modelMat;

    GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
    glEnableVertexAttribArray( vPosition );
    glBindBuffer( GL_ARRAY_BUFFER, obj->g_VBIDvertices);
    glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

    GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
    glEnableVertexAttribArray( vNormal );
    glBindBuffer( GL_ARRAY_BUFFER, obj->g_VBIDnormals);
    glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

    int size;
    glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, obj->g_IBIDelements );
    glGetBufferParameteriv ( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &size );




    // Rendering color
    vec4 objectColor;
    if(g_IntersectionFlag[obj->objID]){
        objectColor = g_ObjectCollisionColor;
    }else{
        objectColor = g_ObjectSelectionColor;
    }

    vec4 ambientProduct = g_LightAmbient * objectColor;
    vec4 diffuseProduct = g_LightDiffuse * objectColor;
    vec4 specularProduct = g_LightSpecular * objectColor;

    glUniform4fv(g_RenderingAmbientID, 1, ambientProduct);
    glUniform4fv(g_RenderingDiffuseID, 1, diffuseProduct);
    glUniform4fv(g_RenderingSpecularID, 1, specularProduct);

    // Now we draw using glDrawElements
    glDrawElements ( GL_TRIANGLES, size/sizeof(GLushort),
        GL_UNSIGNED_SHORT, 0 );



    if(obj->objID == 0){
        if(g_BVVisibleFlag)
        {


            int bSize[5];
            for(int i = 1 ; i < 2; i++){
                setObjectM(obj->boundM[i]);
                GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
                glEnableVertexAttribArray( vPosition );
                glBindBuffer( GL_ARRAY_BUFFER, obj->bound_VBIDvertices[i]);
                glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

                GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
                glEnableVertexAttribArray( vNormal );
                glBindBuffer( GL_ARRAY_BUFFER, obj->bound_VBIDnormals[i]);
                glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

                glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, obj->bound_IBIDelements[i] );
                glGetBufferParameteriv ( GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &bSize[i] );



                vec4 ambientProduct = g_LightAmbient * g_ObjectBoundColor;
                vec4 diffuseProduct = g_LightDiffuse * g_ObjectBoundColor;
                vec4 specularProduct = g_LightSpecular * g_ObjectBoundColor;

                glUniform4fv(g_RenderingAmbientID, 1, ambientProduct);
                glUniform4fv(g_RenderingDiffuseID, 1, diffuseProduct);
                glUniform4fv(g_RenderingSpecularID, 1, specularProduct);

                // Now we draw using glDrawElements
                glDrawElements ( GL_TRIANGLES, size/sizeof(GLushort),
                    GL_UNSIGNED_SHORT, 0 );

            }
        }

    }


}

void GLWidget::drawOBJs( ){
    frame_cnt++;
    //set diffuse color
    setf4("material0.Kd",0.2,0.6,0.2,1);

    mat4 modelMat =  Angel::Scale(0.8,0.8,0.8)*Angel::RotateY(0);


    drawOBJ(&airplane);
    for( int i = 0; i < numOfObstacles1 ; i++){
        drawOBJ(&obstacles1[i]);
    }


}

///////////////////////////////////////////////////////////////////////////////////////////////////////////

void GLWidget::destroyShaders(){
    GLenum ErrorCheckValue = glGetError();

    glUseProgram	( 0 );

    glDetachShader	( g_IDShader, g_vertexShaderID );
    glDetachShader	( g_IDShader, g_fragmentShaderID );

    glDeleteShader	( g_vertexShaderID );
    glDeleteShader	( g_fragmentShaderID );

    glDeleteProgram	( g_IDShader );
}

void GLWidget::destroyVBO(){
    GLenum ErrorCheckValue = glGetError();

    glBindBuffer	( GL_ARRAY_BUFFER, 0 );
    glDeleteBuffers	( 1, &airplane.g_VBIDvertices );
    glDeleteBuffers	( 1, &airplane.g_VBIDnormals );
    glDeleteBuffers	( 1, &airplane.g_IBIDelements );


    for(int i = 0 ; i < 5; i ++){
        glDeleteBuffers	( 1, &airplane.bound_VBIDvertices[i] );
        glDeleteBuffers	( 1, &airplane.bound_VBIDnormals[i] );
        glDeleteBuffers	( 1, &airplane.bound_IBIDelements[i] );

    }

    for(int i=0;i<numOfObstacles1;i++){
        glDeleteBuffers	( 1, &obstacles1[i].g_VBIDvertices );
        glDeleteBuffers	( 1, &obstacles1[i].g_VBIDnormals );
        glDeleteBuffers	( 1, &obstacles1[i].g_IBIDelements );
    }
}

void GLWidget::cleanup(){
    destroyShaders();
    destroyVBO();
}

void GLWidget::idle(int tick){
    if(light_animation){
        //point light animation

        mat4 modelMat = Angel::RotateY(rot);

        glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mlModel" ),1, GL_TRUE, modelMat );
        //spot light animation
        spot_light_lookat = Angel::RotateY(0.5)*spot_light_lookat;
        vec4 spot_light_dir = Angel::normalize(spot_light_lookat - spot_light_position);
        setf4("spot_light.position" ,spot_light_position.x,spot_light_position.y,spot_light_position.z,spot_light_position.w );
        setf4("spot_light.direction", spot_light_dir.x,spot_light_dir.y,spot_light_dir.z,spot_light_dir.w );

        //directional light animation
        modelMat = Angel::RotateZ(-2);
        directional_light_dir = modelMat*directional_light_dir;
        setf4("directional_light.direction", directional_light_dir.x,directional_light_dir.y,directional_light_dir.z,directional_light_dir.w );
    }else{
        mat4 modelMat = Angel::RotateY(rot);
        glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mlModel" ),1, GL_TRUE, modelMat );

        spot_light_lookat = spot_light_lookat;
        vec4 spot_light_dir = Angel::normalize(spot_light_lookat - spot_light_position);
        setf4("spot_light.position" ,spot_light_position.x,spot_light_position.y,spot_light_position.z,spot_light_position.w );
        setf4("spot_light.direction", spot_light_dir.x,spot_light_dir.y,spot_light_dir.z,spot_light_dir.w );

        directional_light_dir = directional_light_dir;
        setf4("directional_light.direction", directional_light_dir.x,directional_light_dir.y,directional_light_dir.z,directional_light_dir.w );

    }
    glUniform1i( glGetUniformLocation( g_IDShader, "direction_toggle" ),direction_toggle );
    glUniform1i( glGetUniformLocation( g_IDShader, "point_toggle" ),point_toggle );
    glUniform1i( glGetUniformLocation( g_IDShader, "spot_toggle" ),spot_toggle );

    glutPostRedisplay();
}



//////Trackball/////////
void GLWidget::trackball(){

    rEye = tEye*cur_eye;
    tEye = Angel::RotateX(0.0); //trackball eye;
    cur_eye = rEye;

    mat4 viewMat = Angel::LookAt( rEye, at, up );
    glUniformMatrix4fv( glGetUniformLocation(
    g_IDShader, "mView" ),1, GL_TRUE, viewMat );

}

void GLWidget::resizeGL(int w, int h){
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0,0,5,0,0,0,0,1,0);
}

void GLWidget::reshape( int width, int height ){
    double dWidth, dHeight;

    if( width >= height ) {
        dWidth = height;
        dHeight = height;
    } else {
        dWidth= width;
        dHeight = width;
    }
    glViewport( ( width - dWidth ) / 2.0, ( height - dHeight ) / 2.0, dWidth, dHeight );
}



void GLWidget::setValue(int what, double val){
	
	fingerFactor = 1;
	switch(gameState){
    case GAME_START:
		switch(nowControl){
		case 0:
			fingerFactor = 0.01;
			dof_acc_val[what] = val * fingerFactor;
			airplane.speedVector[2] = (-dof_acc_val[X_VAL] );
			airplane.speedVector[0] = (dof_acc_val[Z_VAL] );
			airplane.speedVector[1] = (-dof_acc_val[Y_VAL] );
			airplane.updateM();
		case 100:
			break;
		}
		break;
	case GAME_FLYING:
		if(airplane.rs[0] > 1 || airplane.rs[0] < -1){
			airplane.rs[0] /= 1.2;
		}
		else{
			airplane.rs[0] = 0;
			gameState = GAME_FLYING_STABLE;
		}
		break;
	case GAME_FLYING_STABLE:
		switch(nowControl){
		case 0:
			if(what > 2)
				dof_acc_val[what] = val * fingerFactor;
			if(controllerMode == MODE_VERTICAL){
				airplane.rs[0] += (dof_acc_val[ROLL_VAL] )/10; //roll
				airplane.rs[2] += (dof_acc_val[PITCH_VAL] )/10; //pitch
				airplane.rs[1] += (dof_acc_val[YAW_VAL] )/10; //yaw
			}else{
				airplane.rs[1] -= (dof_acc_val[ROLL_VAL] )/10; //roll
				airplane.rs[0] += (dof_acc_val[PITCH_VAL] )/10; //pitch
				airplane.rs[2] -= (dof_acc_val[YAW_VAL] )/10; //yaw
			}
			airplane.updateM();
			dof_acc_val[what] = 0;
			break;
		}
		break;
	}

	/*
    switch(nowControl){
    case 0:
        dof_acc_val[what] = val *= fingerFactor;
        if(what == 5){
            rs[1] -= (dof_acc_val[ROLL_VAL] )/10; //roll
            rs[0] += (dof_acc_val[PITCH_VAL] )/10; //pitch
            rs[2] -= (dof_acc_val[YAW_VAL] )/10; //yaw

            airplane.speedVector[0] = (dof_acc_val[X_VAL] )/5000;
            if(airplane.speedVector[0] > 0.01) airplane.speedVector[0] = 0.01;
            else if(airplane.speedVector[0] < -0.01) airplane.speedVector[0] = -0.01;
            airplane.speedVector[1] = (dof_acc_val[Z_VAL] )/5000;
            if(airplane.speedVector[1] > 0.01) airplane.speedVector[1] = 0.01;
            else if(airplane.speedVector[1] < -0.01) airplane.speedVector[1] = -0.01;
            airplane.speedVector[2] = (dof_acc_val[Y_VAL] )/5000;
            if(airplane.speedVector[2] > 0.1) airplane.speedVector[2] = 0.1;
            else if(airplane.speedVector[2] < -0.1) airplane.speedVector[2] = -0.1;
            airplane.setRotate(rs);
            airplane.updateM();

        }
        break;

    case 11:
        dof_acc_val[what] = val;
        if(what == 5){
            at[3] += dof_acc_val[X_VAL]/5;
            up[3] += dof_acc_val[X_VAL];
            tEye = Angel::RotateX(dof_acc_val[PITCH_VAL])*RotateY(dof_acc_val[ROLL_VAL ])*RotateZ(dof_acc_val[YAW_VAL]);

        }
    }
	*/
}

void GLWidget::setFingerFactor(float val){
    fingerFactor = val;
}

/*
 * [0] : Azimuth
 * [1] : Pitch
 * [2] : Roll
 */
void GLWidget::setPoseFactor(int what, float val){

    int temp = val;

    poseFactor[what] = temp;

    setPose();
}


void GLWidget::restoreModel(){

    airplane.restore();
    for(int i = 0; i < numOfObstacles1; i++){
        obstacles1[i].restore();
    }
}

void GLWidget::restoreView(){
	/*
    at = vec4(airplane.getBoundingBox().getGlobalCenter(), 0);
    up = vec4(airplane.getBoundingBox().getGlobalCenter(),0);
    airplane.default_ts[0] = -xLen/2;
    airplane.default_ts[1] = -zLen/2;
    airplane.default_ts[2] = -yLen/2;

    tEye = Angel::RotateX(0)*Angel::RotateY(180);*/
	setView();
}


void GLWidget::restore(){
	
    if(nowControl == 0 || nowControl == 13){
        restoreModel();
		gameState = GAME_START;
		restoreView();
    }
    else{
		init();
		airplane.restore();
		gameState = GAME_START;
    }
}

// Setting object's modelMatrix
void GLWidget::setModel( void ){
    // Local to World
    mat4 modelMat = Angel::Scale(0.0,0.0,0.0);
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mModel" ), 1, GL_TRUE, modelMat );
}

// Setting light's modelMatrix
void GLWidget::setLight( void ){
    // Lights model matrix
    mat4 lightmodelMat = Angel::RotateY( g_theta );
    glUniformMatrix4fv(	glGetUniformLocation( g_IDShader, "mlModel" ),1, GL_TRUE, lightmodelMat );
}


// Setting object's viewMatrix
void GLWidget::setView( void ){
    // World to Camera(Eye - wc)
    vec4 eye( airplane.ts[0] + 40.0,airplane.ts[0] + 0.0, airplane.ts[0] + 3.0, 0.0 );
    rEye = cur_eye = eye;
    vec4 at ( airplane.ts[0],airplane.ts[0], airplane.ts[0], 0.0 );
    vec4 up ( airplane.rs[0],airplane.rs[0], airplane.rs[0], 0.0 );
    mat4 viewMat = Angel::LookAt(rEye, at, up);
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mView" ),
        1, GL_TRUE, viewMat );
    glUniform4f(glGetUniformLocation(g_IDShader,"viewer_position"),rEye.x,rEye.y,rEye.z,1.0f);
}

// Setting object's projectionMatrix
void GLWidget::setProjection( void ){
    // Camera to Clipping
    mat4 projectionMat = Angel::Perspective( 45.0f,
        (float)g_width/(float)g_height, 1.0f, 100.0f );
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mProjection" ),
        1, GLU_TRUE, projectionMat );
}

void GLWidget::setVAVBObject(){

    // Set VAO
    glGenVertexArrays	( 1, &g_VAID);
    glBindVertexArray	( g_VAID );

    // Set vertex position, normal and elements - VBO

    glGenBuffers	( 1, &airplane.g_VBIDvertices );
    glBindBuffer	( GL_ARRAY_BUFFER, airplane.g_VBIDvertices );
    glBufferData	( GL_ARRAY_BUFFER, airplane.obj.vertices.size() * sizeof(airplane.obj.vertices[0]),	airplane.obj.vertices.data(), GL_STATIC_DRAW );

    glGenBuffers	( 1, &airplane.g_VBIDnormals );
    glBindBuffer	( GL_ARRAY_BUFFER, airplane.g_VBIDnormals );
    glBufferData	( GL_ARRAY_BUFFER, airplane.obj.normals.size() * sizeof(airplane.obj.normals[0]), airplane.obj.normals.data(), GL_STATIC_DRAW );


    if(g_BVVisibleFlag){
        for(int i = 1; i < 2 ; i ++){

            glGenBuffers	( 1, &airplane.bound_VBIDvertices[i] );
            glBindBuffer	( GL_ARRAY_BUFFER, airplane.bound_VBIDvertices[i] );
            glBufferData	( GL_ARRAY_BUFFER, airplane.bound[i].vertices.size() * sizeof(airplane.bound[i].vertices[0]),	airplane.bound[i].vertices.data(), GL_STATIC_DRAW );

            glGenBuffers	( 1, &airplane.bound_VBIDnormals[i] );
            glBindBuffer	( GL_ARRAY_BUFFER, airplane.bound_VBIDnormals[i] );
            glBufferData	( GL_ARRAY_BUFFER, airplane.bound[i].normals.size() * sizeof(airplane.bound[i].normals[0]), airplane.bound[i].normals.data(), GL_STATIC_DRAW );

        }
    }


    for(int i=0;i<numOfObstacles1;i++){
        glGenBuffers	( 1, &obstacles1[i].g_VBIDvertices );
        glBindBuffer	( GL_ARRAY_BUFFER, obstacles1[i].g_VBIDvertices );
        glBufferData	( GL_ARRAY_BUFFER, obstacles1[i].obj.vertices.size() * sizeof(obstacles1[i].obj.vertices[0]),	obstacles1[i].obj.vertices.data(), GL_STATIC_DRAW );

        glGenBuffers	( 1, &obstacles1[i].g_VBIDnormals );
        glBindBuffer	( GL_ARRAY_BUFFER, obstacles1[i].g_VBIDnormals );
        glBufferData	( GL_ARRAY_BUFFER, obstacles1[i].obj.normals.size() * sizeof(obstacles1[i].obj.normals[0]), obstacles1[i].obj.normals.data(), GL_STATIC_DRAW );
    }


    g_BVMesh.loadFromFile("unit_cube.obj");
    glGenBuffers(1, &g_BVMeshVertexBufferID);
    glBindBuffer(GL_ARRAY_BUFFER, g_BVMeshVertexBufferID);
    glBufferData(GL_ARRAY_BUFFER, g_BVMesh.obj.vertices.size() * sizeof(vec4), g_BVMesh.obj.vertices.data(), GL_STATIC_DRAW);

    glGenBuffers(1, &g_BVMeshElementBufferID);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_BVMeshElementBufferID);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, g_BVMesh.obj.elements.size() * sizeof(GLushort), g_BVMesh.obj.elements.data(), GL_STATIC_DRAW);


    //set background
    glGenBuffers(1, &g_VBIDbackground);
    glBindBuffer(GL_ARRAY_BUFFER,g_VBIDbackground);
    glBufferData(GL_ARRAY_BUFFER,background_vertices.size()*sizeof(background_vertices[0]),background_vertices.data(),GL_STATIC_DRAW);

    glGenBuffers(1, &g_VBIDbackground_normals);
    glBindBuffer(GL_ARRAY_BUFFER,g_VBIDbackground_normals);
    glBufferData(GL_ARRAY_BUFFER,background_normals.size()*sizeof(background_normals[0]),background_normals.data(),GL_STATIC_DRAW);


    // Set up the indices buffer - flat shading does not use indices
    glGenBuffers ( 1, &airplane.g_IBIDelements );
    glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, airplane.g_IBIDelements );

    glBufferData ( GL_ELEMENT_ARRAY_BUFFER,
        airplane.obj.elements.size() * sizeof(airplane.obj.elements[0]),
        airplane.obj.elements.data(), GL_STATIC_DRAW );

    GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
    glEnableVertexAttribArray( vPosition );
    glBindBuffer( GL_ARRAY_BUFFER, airplane.g_VBIDvertices);
    glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

    GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
    glEnableVertexAttribArray( vNormal );
    glBindBuffer( GL_ARRAY_BUFFER, airplane.g_VBIDnormals);
    glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );


    if(g_BVVisibleFlag){
        for(int i=1;i<2;i++){
            glGenBuffers ( 1, &airplane.bound_IBIDelements[i] );
            glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, airplane.bound_IBIDelements[i] );

            glBufferData ( GL_ELEMENT_ARRAY_BUFFER,
                airplane.bound[i].elements.size() * sizeof(airplane.bound[i].elements[0]),
                airplane.bound[i].elements.data(), GL_STATIC_DRAW );

            GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
            glEnableVertexAttribArray( vPosition );
            glBindBuffer( GL_ARRAY_BUFFER, airplane.bound_VBIDvertices[i]);
            glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

            GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
            glEnableVertexAttribArray( vNormal );
            glBindBuffer( GL_ARRAY_BUFFER, airplane.bound_VBIDnormals[i]);
            glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );
        }
    }


    for(int i=0;i<numOfObstacles1;i++){
        glGenBuffers ( 1, &obstacles1[i].g_IBIDelements );
        glBindBuffer ( GL_ELEMENT_ARRAY_BUFFER, obstacles1[i].g_IBIDelements );

        glBufferData ( GL_ELEMENT_ARRAY_BUFFER,
            obstacles1[i].obj.elements.size() * sizeof(obstacles1[i].obj.elements[0]),
            obstacles1[i].obj.elements.data(), GL_STATIC_DRAW );



        GLuint vPosition = glGetAttribLocation( g_IDShader, "vPosition" );
        glEnableVertexAttribArray( vPosition );
        glBindBuffer( GL_ARRAY_BUFFER, obstacles1[i].g_VBIDvertices);
        glVertexAttribPointer( vPosition, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

        GLuint vNormal = glGetAttribLocation( g_IDShader, "vNormal" );
        glEnableVertexAttribArray( vNormal );
        glBindBuffer( GL_ARRAY_BUFFER, obstacles1[i].g_VBIDnormals);
        glVertexAttribPointer( vNormal, 3, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0) );

    }
}



void GLWidget::initAirplaneM(){//initialize android matrix
    airplane.objM = Angel::Translate(ts[0],ts[1],ts[2])*Angel::Scale(ss[0],ss[1],ss[2]);

    if(g_BVVisibleFlag){
        for(int i = 1 ; i< 2; i++){
            airplane.boundM[i] = Angel::Translate(ts[0],ts[1],ts[2])*Angel::Scale(ss[0],ss[1],ss[2]);
        }
    }
}
void GLWidget::setObjectM(mat4 modifiedM){
    glUniformMatrix4fv( glGetUniformLocation( g_IDShader, "mModel" ),1, GL_TRUE, modifiedM);
}

void GLWidget::setNowControl(int c){
    switch(c){
    case 0:
        if(nowControl == 1){

        }
        break;
    case 1:
        if(nowControl == 0){

        }
        break;
    case 11:  //camera mode && touched
        clipping = true;
        break;
    case 12: //camera mode but not touched
        clipping = false;
        break;

    case 100:
        if(gameState == GAME_START){
			gameState = GAME_FLYING;
			
		}
    }
    nowControl = c;

}


void GLWidget::drawBoundingBox(){

    for(int i=0; i<g_ObjectList.size(); i++)
    {

        // Get BV information
        vec3 halfWidth = g_ObjectList[i]->getBoundingBox().getHalfWidth();
        vec3 center = g_ObjectList[i]->getBoundingBox().getGlobalCenter();

        g_BVMesh.objM = Angel::Translate(center)*Scale(halfWidth*2)*Angel::RotateY(0);
        g_BVMesh.updateM();
        // Modelview matrix
        mat4 modelViewMat = LookAt(rEye,at,up) * g_BVMesh.objM;
        glUniformMatrix4fv(g_RenderingModelViewMatID, 1, GL_TRUE, modelViewMat);

        glEnableVertexAttribArray(g_RenderingVPositionID);
        glBindBuffer(GL_ARRAY_BUFFER, g_BVMeshVertexBufferID);
        glVertexAttribPointer(g_RenderingVPositionID, 4, GL_FLOAT, GL_FALSE, 0, BUFFER_OFFSET(0));

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_BVMeshVertexBufferID);

        // Rendering color
        glUniform4fv(g_RenderingAmbientID, 1, vec4(0.0, 0.0, 0.0, 1.0));
        glUniform4fv(g_RenderingDiffuseID, 1, vec4(0.0, 0.0, 0.0, 1.0));
        glUniform4fv(g_RenderingSpecularID, 1, vec4(0.0, 0.0, 0.0, 1.0));

        // Draw
        glDrawElements(GL_TRIANGLES, g_BVMesh.obj.elements.size(), GL_UNSIGNED_SHORT, 0);
    }


}
bool GLWidget::testIntersection()
{
    bool isCrash = false;
    for(int i=1; i<g_ObjectList.size(); i++)
    {
        if( i == 1){
            isCrash = true;
            isCrash = true;
            isCrash = false;

        }
        g_IntersectionFlag[0] = g_ObjectList[0]->checkCollision(g_ObjectList[i]);
        if(g_IntersectionFlag[0]){
            /*
            isCrash = true;
            g_ObjectList[0]->rs[1] += 2;
            g_ObjectList[i]->rs[1] += 1;
            g_ObjectList[i]->ss[0] -= 0.4;
            g_ObjectList[i]->ss[1] -= 0.4;
            g_ObjectList[i]->ss[2] -= 0.4;
            */
        }
    }
    return isCrash;
}





void GLWidget::setBoundaryVisible(bool is){
    g_BVVisibleFlag  = is;
}


bool GLWidget::checkStartPose(){
    if(poseFactor[POSE_PITCH] > 25 && poseFactor[POSE_PITCH] < 90){
        if(poseFactor[POSE_ROLL] > 75 && poseFactor[POSE_ROLL] < 105) return true;
        else if(poseFactor[POSE_ROLL] < -75 && poseFactor[POSE_ROLL] > -105) return true;
        else return false;
    }else return false;
}

void GLWidget::setPose(){

	switch(gameState){
	case GAME_START:
		rs[0] = poseFactor[POSE_PITCH]; //pitch
		airplane.setRotate(rs);
		airplane.updateM();
		break;
	}
	//rs[2] = poseFactor[POSE_ROLL]-90; //roll
    //rs[1] = poseFactor[POSE_AZIMUTH]; //yaw

}

void GLWidget::changeCurrentEye(int what, double val){
    switch(what){
    case 0:
        at.x = val;
        break;
    case 1:
        at.y = val;
        break;
    case 2:
        at.z = val;
        break;
    case 3:
        at.w = val;
        break;
    }
}


void GLWidget::setCameraFollow(bool is){
	cameraFollow = is;
}


void GLWidget::setControllerMode(bool is){
	if(is){
		controllerMode =  MODE_HORIZONTAL ;
	}else{
		controllerMode = MODE_VERTICAL;
	}
}


void GLWidget::command(int what){
	switch(what){
	case 1:
		restore();
		break;
	case 2:
		setCameraFollow(true);
		break;
	case 3:
		setCameraFollow(false);
		break;
	}
}
